﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;
using System.Reflection;
using System.Diagnostics;

namespace RoboZip
{
    [Serializable]
    public class SingletonController : MarshalByRefObject
    {

        private static int iPPortNumber;
        private static string className = new StackFrame().GetMethod().DeclaringType.Name.ToString();
        private static TcpChannel TCPChannel = null;
        private static Mutex mutex = null;
        public delegate void ReceiveDelegate(string[] args);
        static private ReceiveDelegate receiver = null;

        public static void SetPortNumber(int IPPortNumber)
        {
            iPPortNumber = IPPortNumber;
        }

        public static ReceiveDelegate Receiver
        {
            get
            {
                return receiver;
            }
            set
            {
                receiver = value;
            }
        }

        public static bool StartSingleInstance(ReceiveDelegate ReceiveDelegate)
        {
            if (AllocateMutex())
            {
                Receiver += ReceiveDelegate;
                return true;
            }
            else
            {
                return false;
            }
        }

        private static bool AllocateMutex()
        {
            string uniqueIdentifier = "{ADC40B16-8FCC-4DFD-B830-716EEA6AD780}";
            mutex = new Mutex(false, uniqueIdentifier);
            try
            {
                if (mutex.WaitOne(1, true))
                {
                    CreateInstanceChannel();
                    return true;
                }
                else
                {
                    mutex.Close();
                    mutex = null;
                    return false;
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        private static void CreateInstanceChannel()
        {
            try
            {
                string assemblyName = Assembly.GetExecutingAssembly().GetName().Name;
                TCPChannel = new TcpChannel(iPPortNumber);
                ChannelServices.RegisterChannel(TCPChannel, false);
                RemotingConfiguration.RegisterWellKnownServiceType(
                Type.GetType(assemblyName + "." + className), className,
                WellKnownObjectMode.SingleCall);
            }
            catch (Exception ex)
            {
                // Possible Exception: Socket Exception because of using port that is already in use.
                throw ex;
            }

        }

        public static void Cleanup(ReceiveDelegate ReceiveDelegate)
        {
            if (mutex != null)
            {
                mutex.Close();
            }

            if (TCPChannel != null)
            {
                TCPChannel.StopListening(null);
            }
            if (ReceiveDelegate != null)
            {
                Receiver -= ReceiveDelegate;
            }
            mutex = null;
            TCPChannel = null;
            ReceiveDelegate = null;
        }

        public static void Send(string[] s)
        {
            SingletonController ctrl;
            TcpChannel channel = new TcpChannel();
            ChannelServices.RegisterChannel(channel, false);
            try
            {
                ctrl = (SingletonController)Activator.GetObject(typeof(SingletonController),
                    "tcp://localhost:" + iPPortNumber.ToString() + "/" + className);
            }
            catch (Exception e)
            {
                Console.WriteLine("Exception: " + e.Message);
                throw;
            }
            ctrl.Receive(s);
        }

        public void Receive(string[] s)
        {
            if (receiver != null)
            {
                receiver(s);
            }
        }
    }
}
